#ifndef __THREADPOOL_H__
#define __THREADPOOL_H__

#include <mutex>
#include <thread>
#include <future>
#include <condition_variable>
#include <functional>
#include <vector>
#include <queue>

namespace NetWork {
    class ThreadPool {
    private:
        using Task = std::function<void()>;

    private:
        std::queue<Task> Tasks;
        std::vector<std::thread> Threads;

        std::mutex Mutex;
        std::condition_variable Productor;
        std::condition_variable Consumer;

        size_t MaxTask;
        bool bShutDown;
        bool bWaitAllTasksComplete;

    public:
        ThreadPool(size_t threadCounts = std::thread::hardware_concurrency(),
                size_t maxTask = 1024,
                bool waitAllTasksComplete = false);

        ~ThreadPool();

    public:
        template<typename CallableObj, typename... Args>
        auto AddTask(CallableObj&& pre, Args&&... args)
        {
            std::unique_lock<std::mutex> lock(Mutex);

            Productor.wait(lock, [&Tasks = Tasks, &MaxTask = MaxTask](){
                return Tasks.size() < MaxTask;
            });

            if (bShutDown)
                return std::future<decltype(pre(std::forward<Args>(args)...))>();

            //参数类型擦除
            std::function<decltype(pre(std::forward<Args>(args)...))()> func = 
                std::bind(std::forward<CallableObj>(pre), std::forward<Args>(args)...);

            //std::packaged_task 不可复制
            //导致拥有 std::packaged_task 捕获的 lambda 不能够初始化为 std::function (需要可以能够拷贝的成员)
            /*
                std_function.h:439:69: error: static assertion failed: std::function target must be copy-constructible
                    static_assert(is_copy_constructible<__decay_t<_Functor>>::value,
            */
            //只能够选择初始化为共享指针
            auto packageTask = std::make_shared<std::packaged_task<decltype(pre(std::forward<Args>(args)...))()>>
                (
                    func
                );

            //返回值类型擦除
            Tasks.push([packageTask](){
                (*packageTask)();
            });

            Consumer.notify_one();
            return (*packageTask).get_future();
        }
    };
}


#endif